package com.mrzhou.gateway.handler;

import com.mrzhou.core.tool.utils.Func;
import com.mrzhou.gateway.provider.ResponseProvider;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.*;
import org.springframework.web.server.ResponseStatusException;
import reactor.core.publisher.Mono;

import java.io.FileNotFoundException;
import java.util.Map;

public class ErrorExceptionHandler extends DefaultErrorWebExceptionHandler {

    private final String SWAGGER_API_DOCS = "/v2/api-docs";

    public ErrorExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    }

    /**
     * 获取异常的属性
     */
    @Override
    protected Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
        int code = HttpStatus.INTERNAL_SERVER_ERROR.value();
        Throwable t = super.getError(request);
        if(t instanceof NotFoundException) {
            code = HttpStatus.NOT_FOUND.value();
        }
        if (t instanceof ResponseStatusException) {
            code = ((ResponseStatusException) t).getStatus().value();
        }

        return ResponseProvider.response(code, this.buildMessage(request, t));
    }

    @Override
    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        Map<String, Object> error = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
        int status = this.getHttpStatus(error);
        // 当请求为 SWAGGER_API_DOCS 时返回200状态码, 防止swagger的doc页面加载失败!
        if (request.path().endsWith(SWAGGER_API_DOCS)) {
            status = HttpStatus.OK.value();
        }
        return ServerResponse.status(status).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(error));
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    @Override
    protected int getHttpStatus(Map<String, Object> errorAttributes) {
        return errorAttributes.containsKey("code") ? (int) errorAttributes.get("code") : super.getHttpStatus(errorAttributes);
    }

    private String buildMessage(ServerRequest request, Throwable ex) {
        StringBuilder message = new StringBuilder("Failed to handle request 【");
        message.append(request.methodName());
        message.append(" ");
        message.append(request.uri());
        message.append("】");
        if (ex != null) {
            message.append("：");
            message.append(ex.getMessage());
        }
        return message.toString();
    }

}
